home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / jade / src / unix_misc.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  18KB  |  741 lines

  1. /* unix_misc.c -- Miscellaneous functions for Unix
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4.    This file is part of Jade.
  5.  
  6.    Jade is free software; you can redistribute it and/or modify it
  7.    under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    Jade is distributed in the hope that it will be useful, but
  12.    WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with Jade; see the file COPYING.    If not, write to
  18.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22.  
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <sys/stat.h>
  26. #include <time.h>
  27. #include <unistd.h>
  28. #include <fcntl.h>
  29. #include <dirent.h>
  30. #include <pwd.h>
  31. #include <netdb.h>
  32.  
  33. #ifdef HAVE_STRERROR
  34. # include <errno.h>
  35. #else
  36.   extern int sys_nerr, errno;
  37.   extern char *sys_errlist[];
  38. #endif
  39.  
  40. #ifdef ENVIRON_UNDEFINED
  41.   extern char **environ;
  42. #endif
  43.  
  44. _PR bool file_exists(u_char *);
  45. _PR u_long file_mod_time(u_char *);
  46. _PR void sys_misc_init(void);
  47.  
  48. _PR bool same_files(u_char *, u_char *);
  49. _PR u_char * file_part(u_char *);
  50. _PR VALUE lookup_errno(void);
  51. _PR void doconmsg(u_char *);
  52. _PR VALUE read_file(u_char *);
  53. _PR u_long sys_time(void);
  54. _PR int add_file_part(u_char *, const u_char *, int);
  55. _PR VALUE sys_expand_file_name(VALUE);
  56. _PR VALUE sys_fully_qualify_file_name(VALUE);
  57.  
  58. bool
  59. same_files(u_char *file1, u_char *file2)
  60. {
  61.     bool rc = FALSE;
  62.     struct stat stat1, stat2;
  63.     if(!stat(file1, &stat1))
  64.     {
  65.     if(!stat(file2, &stat2))
  66.     {
  67.         if((stat1.st_dev == stat2.st_dev)
  68.            && (stat1.st_ino == stat2.st_ino))
  69.         {
  70.         rc = TRUE;
  71.         }
  72.     }
  73.     }
  74.     else
  75.     rc = !strcmp(file1, file2);
  76.     return(rc);
  77. }
  78.  
  79. u_char *
  80. file_part(u_char *fname)
  81. {
  82.     u_char *tmp = strrchr(fname, '/');
  83.     if(tmp)
  84.     return(tmp + 1);
  85.     return(fname);
  86. }
  87.  
  88. VALUE
  89. lookup_errno(void)
  90. {
  91. #ifdef HAVE_STRERROR
  92.     return(string_dup(strerror(errno)));
  93. #else
  94.     if(errno >= sys_nerr)
  95.         return(string_dup(sys_errlist[errno]));
  96.     else
  97.         return(MKSTR("<error>"));
  98. #endif
  99. }
  100.  
  101. void
  102. doconmsg(u_char *msg)
  103. {
  104.     fputs(msg, stderr);
  105. }
  106.  
  107. VALUE
  108. read_file(u_char *fileName)
  109. {
  110.     FILE *fh = fopen(fileName, "r");
  111.     if(fh)
  112.     {
  113.     struct stat stat;
  114.     if(!fstat(fileno(fh), &stat))
  115.     {
  116.         VALUE mem = make_string(stat.st_size + 1);
  117.         if(mem)
  118.         {
  119.         fread(VSTR(mem), 1, stat.st_size, fh);
  120.         VSTR(mem)[stat.st_size] = 0;
  121.         fclose(fh);
  122.         return(mem);
  123.         }
  124.         else
  125.         mem_error();
  126.     }
  127.     fclose(fh);
  128.     }
  129.     return(cmd_signal(sym_file_error,
  130.               list_2(lookup_errno(), string_dup(fileName))));
  131. }
  132.  
  133. u_long
  134. sys_time(void)
  135. {
  136.     return(time(NULL));
  137. }
  138.  
  139. int
  140. add_file_part(u_char *buf, const u_char *part, int bufLen)
  141. {
  142.     int bufend = strlen(buf);
  143.     int partlen = strlen(part);
  144.     if((bufend > 0) && (buf[bufend-1] != '/') && (*part != '/'))
  145.     {
  146.     if(++bufend >= bufLen)
  147.         return(FALSE);
  148.     buf[bufend-1] = '/';
  149.     buf[bufend] = 0;
  150.     }
  151.     if((bufend + partlen) >= bufLen)
  152.     return(FALSE);
  153.     strcpy(buf + bufend, part);
  154.     return(TRUE);
  155. }
  156.  
  157. _PR VALUE cmd_delete_file(VALUE file);
  158. DEFUN_INT("delete-file", cmd_delete_file, subr_delete_file, (VALUE file), V_Subr1, DOC_delete_file, "fDelete file:") /*
  159. ::doc:delete_file::
  160. delete-file FILE-NAME
  161.  
  162. Attempts to delete the file called FILE-NAME.
  163. ::end:: */
  164. {
  165.     DECLARE1(file, STRINGP);
  166.     if(!unlink(VSTR(file)))
  167.     return(sym_t);
  168.     return(signal_file_error(file));
  169. }
  170.  
  171. _PR VALUE cmd_rename_file(VALUE src, VALUE dst);
  172. DEFUN_INT("rename-file", cmd_rename_file, subr_rename_file, (VALUE src, VALUE dst), V_Subr2, DOC_rename_file, "fRename file:\nFRename file `%s' as:") /*
  173. ::doc:rename_file::
  174. rename-file SRC DEST
  175.  
  176. Tries to rename the file SRC as DEST, this doesn't work across filesystems, or
  177. if a file DEST already exists.
  178. ::end:: */
  179. {
  180.     DECLARE1(src, STRINGP);
  181.     DECLARE2(dst, STRINGP);
  182.     if(!rename(VSTR(src), VSTR(dst)))
  183.     return(sym_t);
  184.     return(signal_file_error(list_2(src, dst)));
  185. }
  186.  
  187. _PR VALUE cmd_copy_file(VALUE src, VALUE dst);
  188. DEFUN_INT("copy-file", cmd_copy_file, subr_copy_file, (VALUE src, VALUE dst), V_Subr2, DOC_copy_file, "fCopy file:\nFCopy file `%s' to:") /*
  189. ::doc:copy_file::
  190. copy-file SRC DEST
  191.  
  192. Copies the file called SRC to the file DEST.
  193. ::end:: */
  194. {
  195.     VALUE res = sym_t;
  196.     int srcf;
  197.     DECLARE1(src, STRINGP);
  198.     DECLARE2(dst, STRINGP);
  199.     srcf = open(VSTR(src), O_RDONLY);
  200.     if(srcf != -1)
  201.     {
  202.     int dstf = open(VSTR(dst), O_WRONLY | O_CREAT | O_TRUNC, 0666);
  203.     if(dstf != -1)
  204.     {
  205.         struct stat statb;
  206.         int rd;
  207.         if(fstat(srcf, &statb) == 0)
  208.         chmod(VSTR(dst), statb.st_mode);
  209.         do {
  210.         u_char buf[BUFSIZ];
  211.         int wr;
  212.         rd = read(srcf, buf, BUFSIZ);
  213.         if(rd < 0)
  214.         {
  215.             res = signal_file_error(src);
  216.             break;
  217.         }
  218.         wr = write(dstf, buf, rd);
  219.         if(wr != rd)
  220.         {
  221.             res = signal_file_error(dst);
  222.             break;
  223.         }
  224.         } while(rd != 0);
  225.         close(dstf);
  226.     }
  227.     else
  228.         res = signal_file_error(dst);
  229.     close(srcf);
  230.     }
  231.     else
  232.     res = signal_file_error(src);
  233.     return(res);
  234. }
  235.  
  236. _PR VALUE cmd_file_readable_p(VALUE file);
  237. DEFUN("file-readable-p", cmd_file_readable_p, subr_file_readable_p, (VALUE file), V_Subr1, DOC_file_readable_p) /*
  238. ::doc:file_readable_p::
  239. file-readable-p FILE
  240.  
  241. Returns t if FILE available for reading from.
  242. ::end:: */
  243. {
  244.     DECLARE1(file, STRINGP);
  245.     if(!access(VSTR(file), R_OK))
  246.     return(sym_t);
  247.     return(sym_nil);
  248. }
  249.  
  250. _PR VALUE cmd_file_writable_p(VALUE file);
  251. DEFUN("file-writable-p", cmd_file_writable_p, subr_file_writable_p, (VALUE file), V_Subr1, DOC_file_writeable_p) /*
  252. ::doc:file_writeable_p::
  253. file-writable-p FILE
  254.  
  255. Returns t if FILE available for writing to.
  256. ::end:: */
  257. {
  258.     DECLARE1(file, STRINGP);
  259.     if(!access(VSTR(file), W_OK))
  260.     return(sym_t);
  261.     return(sym_nil);
  262. }
  263.  
  264. _PR VALUE cmd_file_exists_p(VALUE file);
  265. DEFUN("file-exists-p", cmd_file_exists_p, subr_file_exists_p, (VALUE file), V_Subr1, DOC_file_exists_p) /*
  266. ::doc:file_exists_p::
  267. file-exists-p FILE
  268.  
  269. Returns t if FILE exists.
  270. ::end:: */
  271. {
  272.     DECLARE1(file, STRINGP);
  273.     if(!access(VSTR(file), F_OK))
  274.     return(sym_t);
  275.     return(sym_nil);
  276. }
  277. bool
  278. file_exists(u_char *fileName)
  279. {
  280.     if(!access(fileName, F_OK))
  281.     {
  282.     struct stat statb;
  283.     if(!stat(fileName, &statb) && !S_ISDIR(statb.st_mode))
  284.         return(TRUE);
  285.     }
  286.     return(FALSE);
  287. }
  288.  
  289. _PR VALUE cmd_file_regular_p(VALUE file);
  290. DEFUN("file-regular-p", cmd_file_regular_p, subr_file_regular_p, (VALUE file), V_Subr1, DOC_file_regular_p) /*
  291. ::doc:file_regular_p::
  292. file-regular-p FILE
  293.  
  294. Returns t if FILE is a ``normal'' file, ie, not a directory, device, symbolic
  295. link, etc...
  296. ::end:: */
  297. {
  298.     struct stat statb;
  299.     DECLARE1(file, STRINGP);
  300.     if(!stat(VSTR(file), &statb))
  301.     {
  302.     if(S_ISREG(statb.st_mode))
  303.         return(sym_t);
  304.     }
  305.     return(sym_nil);
  306. }
  307.  
  308. _PR VALUE cmd_file_directory_p(VALUE file);
  309. DEFUN("file-directory-p", cmd_file_directory_p, subr_file_directory_p, (VALUE file), V_Subr1, DOC_file_directory_p) /*
  310. ::doc:file_directory_p::
  311. file-directory-p FILE
  312.  
  313. Returns t if FILE is a directory.
  314. ::end:: */
  315. {
  316.     struct stat statb;
  317.     DECLARE1(file, STRINGP);
  318.     if(!stat(VSTR(file), &statb))
  319.     {
  320.     if(S_ISDIR(statb.st_mode))
  321.         return(sym_t);
  322.     }
  323.     return(sym_nil);
  324. }
  325.  
  326. _PR VALUE cmd_file_symlink_p(VALUE file);
  327. DEFUN("file-symlink-p", cmd_file_symlink_p, subr_file_symlink_p, (VALUE file), V_Subr1, DOC_file_symlink_p) /*
  328. ::doc:file_symlink_p::
  329. file-symlink-p FILE
  330.  
  331. Returns t if FILE is a symbolic link to another file.
  332. ::end:: */
  333. {
  334.     struct stat statb;
  335.     DECLARE1(file, STRINGP);
  336.     if(!stat(VSTR(file), &statb))
  337.     {
  338.     if(S_ISLNK(statb.st_mode))
  339.         return(sym_t);
  340.     }
  341.     return(sym_nil);
  342. }
  343.  
  344. _PR VALUE cmd_file_owner_p(VALUE file);
  345. DEFUN("file-owner-p", cmd_file_owner_p, subr_file_owner_p, (VALUE file), V_Subr1, DOC_file_owner_p) /*
  346. ::doc:file_owner_p::
  347. file-owner-p FILE
  348.  
  349. Returns t if the ownership (uid & gid) of file FILE (a string) is the same
  350. as that of any files written by the editor.
  351. ::end:: */
  352. {
  353.     struct stat statb;
  354.     DECLARE1(file, STRINGP);
  355.     if(!stat(VSTR(file), &statb))
  356.     {
  357.     if((statb.st_uid == geteuid()) && (statb.st_gid == getegid()))
  358.         return(sym_t);
  359.     }
  360.     return(sym_nil);
  361. }
  362.  
  363. _PR VALUE cmd_file_nlinks(VALUE file);
  364. DEFUN("file-nlinks", cmd_file_nlinks, subr_file_nlinks, (VALUE file), V_Subr1, DOC_file_nlinks) /*
  365. ::doc:file_nlinks::
  366. file-nlinks FILE
  367.  
  368. Returns the number of links pointing to the file called FILE. This will be
  369. one if FILE has only one name. Doesn't count symbolic links.
  370. ::end:: */
  371. {
  372.     struct stat statb;
  373.     DECLARE1(file, STRINGP);
  374.     if(!stat(VSTR(file), &statb))
  375.     return(make_number(statb.st_nlink));
  376.     return(sym_nil);
  377. }
  378.  
  379. _PR VALUE cmd_file_modes(VALUE file);
  380. DEFUN("file-modes", cmd_file_modes, subr_file_modes, (VALUE file), V_Subr1, DOC_file_modes) /*
  381. ::doc:file_modes::
  382. file-modes FILE
  383.  
  384. Return the access permissions of the file called FILE, an integer. Note that
  385. the format of this integer is not defined, it differs from system to system.
  386. ::end:: */
  387. {
  388.     struct stat statb;
  389.     DECLARE1(file, STRINGP);
  390.     if(!stat(VSTR(file), &statb))
  391.     return(make_number(statb.st_mode));
  392.     return(sym_nil);
  393. }
  394.  
  395. _PR VALUE cmd_set_file_modes(VALUE file, VALUE modes);
  396. DEFUN("set-file-modes", cmd_set_file_modes, subr_set_file_modes, (VALUE file, VALUE modes), V_Subr2, DOC_set_file_modes) /*
  397. ::doc:set_file_modes::
  398. set-file-modes FILE MODES
  399.  
  400. Sets the access permissions of FILE to MODES, an integer. The only real way
  401. you can get this integer is from `file-modes' since it changes from system
  402. to system.
  403. ::end:: */
  404. {
  405.     DECLARE1(file, STRINGP);
  406.     DECLARE2(modes, NUMBERP);
  407.     if(chmod(VSTR(file), VNUM(modes)) == 0)
  408.     return(modes);
  409.     return(signal_file_error(file));
  410. }
  411.  
  412. u_long
  413. file_mod_time(u_char *file)
  414. {
  415.     struct stat statb;
  416.     if(!stat(file, &statb))
  417.     return(statb.st_mtime);
  418.     return(0);
  419. }
  420. _PR VALUE cmd_file_modtime(VALUE file);
  421. DEFUN("file-modtime", cmd_file_modtime, subr_file_modtime, (VALUE file), V_Subr1, DOC_file_modtime) /*
  422. ::doc:file_modtime::
  423. file-modtime FILE
  424.  
  425. Return the time (an integer) that FILE was last modified.
  426. ::end:: */
  427. {
  428.     DECLARE1(file, STRINGP);
  429.     return(make_number(file_mod_time(VSTR(file))));
  430. }
  431.  
  432. _PR VALUE cmd_directory_files(VALUE dirname);
  433. DEFUN("directory-files", cmd_directory_files, subr_directory_files, (VALUE dirname), V_Subr1, DOC_directory_files) /*
  434. ::doc:directory_files::
  435. directory-files DIRECTORY
  436.  
  437. Returns a list of the names of all files in directory DIRECTORY, directories
  438. in DIRECTORY have a `/' character appended to their name.
  439. ::end:: */
  440. {
  441.     DIR *dir;
  442.     u_char *dname;
  443.     DECLARE1(dirname, STRINGP);
  444.     dname = VSTR(dirname);
  445.     if(*dname == 0)
  446.     dname = ".";
  447.     dir = opendir(dname);
  448.     if(dir)
  449.     {
  450.     VALUE list = sym_nil;
  451.     struct dirent *de;
  452.     while((de = readdir(dir)))
  453.     {
  454.         VALUE name;
  455.         if(!((name = string_dup(de->d_name)) && (list = cmd_cons(name, list))))
  456.         {
  457.         mem_error();
  458.         closedir(dir);
  459.         return(NULL);
  460.         }
  461.     }
  462.     closedir(dir);
  463.     return(list);
  464.     }
  465.     return(cmd_signal(sym_file_error, list_2(lookup_errno(), dirname)));
  466. }
  467.  
  468. _PR VALUE cmd_user_login_name(void);
  469. DEFUN("user-login-name", cmd_user_login_name, subr_user_login_name, (void), V_Subr0, DOC_user_login_name) /*
  470. ::doc:user_login_name::
  471. user-login-name
  472.  
  473. Returns the login name of the user (a string).
  474. On the Amiga this is taken from the environment variable `USERNAME'.
  475. ::end:: */
  476. {
  477.     /* Just look this up once, then use the saved copy.     */
  478.     static VALUE user_login_name;
  479.     char *tmp;
  480.     if(user_login_name)
  481.     return(user_login_name);
  482.     if(!(tmp = getlogin()))
  483.     {
  484.     struct passwd *pwd;
  485.     if(!(pwd = getpwuid(geteuid())))
  486.         return(NULL);
  487.     tmp = pwd->pw_name;
  488.     }
  489.     user_login_name = string_dup(tmp);
  490.     mark_static(&user_login_name);
  491.     return(user_login_name);
  492. }
  493.  
  494. _PR VALUE cmd_user_full_name(void);
  495. DEFUN("user-full-name", cmd_user_full_name, subr_user_full_name, (void), V_Subr0, DOC_user_full_name) /*
  496. ::doc:user_full_name::
  497. user-full-name
  498.  
  499. Returns the real name of the user (a string).
  500. On the Amiga this is taken from the environment variable `REALNAME'.
  501. ::end:: */
  502. {
  503.     struct passwd *pwd;
  504.     static VALUE user_full_name;
  505.     if(user_full_name)
  506.     return(user_full_name);
  507.     if(!(pwd = getpwuid(geteuid())))
  508.     return(NULL);
  509. #ifndef FULL_NAME_TERMINATOR
  510.     user_full_name = string_dup(pwd->pw_gecos);
  511. #else
  512.     {
  513.     char *end;
  514.     if(end = strchr(pwd->pw_gecos, FULL_NAME_TERMINATOR))
  515.         user_full_name = string_dupn(pwd->pw_gecos, end - pwd->pw_gecos);
  516.     else
  517.         user_full_name = string_dup(pwd->pw_gecos);
  518.     }
  519. #endif
  520.     mark_static(&user_full_name);
  521.     return(user_full_name);
  522. }
  523.  
  524. _PR VALUE cmd_user_home_directory(void);
  525. DEFUN("user-home-directory", cmd_user_home_directory, subr_user_home_directory, (void), V_Subr0, DOC_user_home_directory) /*
  526. ::doc:user_home_directory::
  527. user-home-directory
  528.  
  529. Returns the user's home directory (a string). It will be terminated by a slash
  530. (or whatever is appropriate) so that it can be glued together with a file name
  531. making a valid path name.
  532.  
  533. The first place we look for the directory name is the `HOME' environment
  534. variable. If this doesn't exist we either use the `SYS:' assignment when
  535. on AmigaDOS or look in the /etc/passwd file if on Unix.
  536. ::end:: */
  537. {
  538.     static VALUE user_home_directory;
  539.     if(user_home_directory)
  540.     return(user_home_directory);
  541.     else
  542.     {
  543.     char *src;
  544.     int len;
  545.     src = getenv("HOME");
  546.     if(!src)
  547.     {
  548.         struct passwd *pwd;
  549.         pwd = getpwuid(geteuid());
  550.         if(!pwd || !pwd->pw_dir)
  551.         {
  552.         return(cmd_signal(sym_error, LIST_1(MKSTR("Can't find your home directory"))));
  553.         }
  554.         src = pwd->pw_dir;
  555.     }
  556.     len = strlen(src);
  557.     if(src[len] != '/')
  558.     {
  559.         user_home_directory = string_dupn(src, len + 1);
  560.         VSTR(user_home_directory)[len] = '/';
  561.         VSTR(user_home_directory)[len+1] = 0;
  562.     }
  563.     else
  564.         user_home_directory = string_dup(src);
  565.     mark_static(&user_home_directory);
  566.     return(user_home_directory);
  567.     }
  568. }
  569.  
  570. _PR VALUE cmd_system_name(void);
  571. DEFUN("system-name", cmd_system_name, subr_system_name, (void), V_Subr0,  DOC_system_name) /*
  572. ::doc:system_name::
  573. system-name
  574.  
  575. Returns the name of the host which the editor is running on.
  576. On the Amiga this is taken from the environment variable `HOSTNAME'.
  577. ::end:: */
  578. {
  579.     u_char buf[128];
  580.     struct hostent *h;
  581.     static VALUE system_name;
  582.     if(system_name)
  583.     return(system_name);
  584.     if(gethostname(buf, 128))
  585.     return(NULL);
  586.     h = gethostbyname(buf);
  587.     if(h)
  588.     system_name = string_dup((u_char *)h->h_name);
  589.     else
  590.     system_name = string_dup(buf);
  591.     mark_static(&system_name);
  592.     return(system_name);
  593. }
  594.  
  595. _PR VALUE cmd_setenv(VALUE name, VALUE val);
  596. DEFUN("setenv", cmd_setenv, subr_setenv, (VALUE name, VALUE val), V_Subr2, DOC_setenv) /*
  597. ::doc:setenv::
  598. setenv VARIABLE [VALUE]
  599.  
  600. Sets the value of the environment variable called VARIABLE (a string) to
  601. the string VALUE. If VALUE is undefined or nil the variable is removed from
  602. the environment.
  603. ::end:: */
  604. {
  605.     /* This function was adapted from the GNU libc putenv() function,
  606.        hopefully it's portable... 
  607.          It's main problem is that it doesn't know whether or not to
  608.        unallocate the strings making up the environment when they're
  609.        finished with.  */
  610.     char **ep;
  611.     int name_len;
  612.     DECLARE1(name, STRINGP);
  613.     name_len = STRING_LEN(name);
  614.     if(!STRINGP(val))
  615.     {
  616.     /* remove the variable NAME */
  617.     for(ep = environ; *ep != NULL; ep++)
  618.     {
  619.         if((strncmp(VSTR(name), *ep, name_len) == 0)
  620.            && ((*ep)[name_len] == '='))
  621.         {
  622.         while(ep[1] != NULL)
  623.         {
  624.             ep[0] = ep[1];
  625.             ep++;
  626.         }
  627.         *ep = NULL;
  628.         return(sym_t);
  629.         }
  630.     }
  631.     return(sym_nil);
  632.     }
  633.     else
  634.     {
  635.     /* setting NAME to VAL */
  636.     int size;
  637.     char *env_string = str_alloc(name_len + 1 + STRING_LEN(val));
  638.     if(!env_string)
  639.         return(mem_error());
  640.     memcpy(env_string, VSTR(name), name_len);
  641.     env_string[name_len] = '=';
  642.     strcpy(env_string + name_len + 1, VSTR(val));
  643.     for(ep = environ, size = 0; *ep != NULL; ep++)
  644.     {
  645.         if((strncmp(VSTR(name), *ep, name_len) == 0)
  646.            && ((*ep)[name_len] == '='))
  647.         break;
  648.         size++;
  649.     }
  650.     if(*ep == NULL)
  651.     {
  652.         /* NAME not in list, create new entry */
  653.         static char **last_environ = NULL;
  654.         char **new_environ = str_alloc((size + 2) * sizeof(char *));
  655.         if(!new_environ)
  656.         return(mem_error());
  657.         memcpy(new_environ, environ, size * sizeof(char *));
  658.         new_environ[size] = env_string;
  659.         new_environ[size + 1] = NULL;
  660.         if(last_environ != NULL)
  661.         str_free(last_environ);
  662.         last_environ = environ = new_environ;
  663.     }
  664.     else
  665.     {
  666.         /* adjust existing entry */
  667.         *ep = env_string;
  668.     }
  669.     return(val);
  670.     }
  671. }
  672.  
  673. VALUE
  674. sys_expand_file_name(VALUE namev)
  675. {
  676.     u_char *name = VSTR(namev);
  677.     if(*name == '~')
  678.     {
  679.     VALUE home = cmd_user_home_directory();
  680.     if(!home || !STRINGP(home))
  681.         return(NULL);
  682.     switch(name[1])
  683.     {
  684.     case 0:
  685.         return(home);
  686.     case '/':
  687.         {
  688.         u_char buf[512];
  689.         strcpy(buf, VSTR(home));
  690.         if(!add_file_part(buf, name + 2, 512))
  691.             return(NULL);
  692.         return(string_dup(buf));
  693.         }
  694.     default:
  695.         return(cmd_signal(sym_file_error,
  696.                   list_2(MKSTR("Can't expand"), namev)));
  697.     }
  698.     }
  699.     return(namev);
  700. }
  701.  
  702. VALUE
  703. sys_fully_qualify_file_name(VALUE name)
  704. {
  705.     u_char buf[512];
  706.     if(*VSTR(name) == '/')
  707.     /* starting from root. */
  708.     return(name);
  709.     if(getcwd(buf, 512) != NULL)
  710.     {
  711.     if(add_file_part(buf, VSTR(name), 512))
  712.         return(string_dup(buf));
  713.     }
  714.     return(NULL);
  715. }
  716.  
  717. void
  718. sys_misc_init(void)
  719. {
  720.     ADD_SUBR(subr_delete_file);
  721.     ADD_SUBR(subr_rename_file);
  722.     ADD_SUBR(subr_copy_file);
  723.     ADD_SUBR(subr_file_readable_p);
  724.     ADD_SUBR(subr_file_writable_p);
  725.     ADD_SUBR(subr_file_exists_p);
  726.     ADD_SUBR(subr_file_regular_p);
  727.     ADD_SUBR(subr_file_directory_p);
  728.     ADD_SUBR(subr_file_symlink_p);
  729.     ADD_SUBR(subr_file_owner_p);
  730.     ADD_SUBR(subr_file_nlinks);
  731.     ADD_SUBR(subr_file_modes);
  732.     ADD_SUBR(subr_set_file_modes);
  733.     ADD_SUBR(subr_file_modtime);
  734.     ADD_SUBR(subr_directory_files);
  735.     ADD_SUBR(subr_user_login_name);
  736.     ADD_SUBR(subr_user_full_name);
  737.     ADD_SUBR(subr_user_home_directory);
  738.     ADD_SUBR(subr_system_name);
  739.     ADD_SUBR(subr_setenv);
  740. }
  741.